package style.zcyLeetCode.a_0006_图;

import java.util.*;

/**
 * 拓扑排序
 * 1、以入度为0的作为出发点
 * 2、
 */
public class TopologySort {

    // 点
    public static class Node {
        public int value; // 数据项（点上的值）
        public int in;  // 入度
        public int out; // 出度
        public ArrayList<Node> nexts; // 当前点的直接邻居有哪些点
        public ArrayList<Edge> edges; // 当前点的直接连接的边（由当前点发散出去的边）

        public Node(int value) {
            this.value = value;
            in = 0;
            out = 0;
            nexts = new ArrayList<>();
            edges = new ArrayList<>();
        }
    }

    // 边
    public static class Edge {
        public int weight; // 边的权值
        public Node from;  // 表示有向边
        public Node to;    // 表示有向边

        public Edge(int weight, Node from, Node to) {
            this.weight = weight;
            this.from = from;
            this.to = to;
        }
    }

    // 图
    public static class Graph {
        public HashMap<Integer, Node> nodes;  // 点集
        public HashSet<Edge> edges;           // 边集

        public Graph() {
            nodes = new HashMap<>();
            edges = new HashSet<>();
        }
    }

    /**
     * 拓扑排序算法
     * @param graph 有向图
     * @return 列表
     */
    public static List<Node> sortedTopology(Graph graph) {
        HashMap<Node, Integer> inMap = new HashMap<>(); // 存放所有节点的入度关系
        Queue<Node> zeroInQuere = new LinkedList<>();   // 存放入度为零的节点

        for (Node node : graph.nodes.values()) {
            inMap.put(node, node.in);
            if (node.in == 0) {
                zeroInQuere.add(node);
            }
        }

        // 返回的list
        List<Node> result = new ArrayList<>();
        while (!zeroInQuere.isEmpty()) {
            Node cur = zeroInQuere.poll();
            result.add(cur);
            // 把当前节点关联的边都删掉
            for (Node next : cur.nexts) {
                inMap.put(next, inMap.get(next)-1);
                if (inMap.get(next) == 0) {
                    zeroInQuere.add(next);
                }
            }
        }
        return result;
    }

}
